package com.zhoug.common.media.m3u8;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;


import com.zhoug.common.utils.FileUtils;
import com.zhoug.common.utils.IOUtils;
import com.zhoug.common.utils.LogUtils;
import com.zhoug.common.utils.ThreadUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;


/**
 * m3u8文件工具
 *
 * @Author 35574
 * @Date 2020/5/4
 * @Description
 */
public class M3u8Downloader {
    private static final String TAG = ">>>M3u8Downloader";
    private static final String CHARSET = "UTF-8";
    private String url;
    private volatile boolean downSwitch = false;//下载开关
    private volatile boolean downing = false;
    private String path;//合成的视频保存的路径
    private List<String> finishUrl = new ArrayList<>();//已经下载完成的tsurl
    private volatile long finishSize = 0;//已经下载的文件总大小


    private Callback callback;

    private static final int CALLBACK = 100;
    private static final int CALLBACK_ARGS1_START = 101;
    private static final int CALLBACK_ARGS1_PROGRESS = 102;
    private static final int CALLBACK_ARGS1_STOP = 103;
    private static final int CALLBACK_ARGS1_ERROR = 104;

    //主线程回掉
    private Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case CALLBACK:
                    //下载状态回掉
                    if (callback != null) {
                        if (msg.arg1 == CALLBACK_ARGS1_START) {
                            callback.onStart();
                        } else if (msg.arg1 == CALLBACK_ARGS1_PROGRESS) {
                            callback.onProgress(finishSize);
                        } else if (msg.arg1 == CALLBACK_ARGS1_STOP) {
                            callback.onStop(path);
                            mainHandler.removeMessages(CALLBACK);
                        } else if (msg.arg1 == CALLBACK_ARGS1_ERROR) {
                            callback.onError((String) msg.obj);
                        }
                    }

                    break;


            }
            return true;
        }
    });

    public M3u8Downloader() {
    }

    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * 解析m3u8文件,获取到ts文件url集合
     *
     * @param url m3u8文件地址 eg:http://112.45.116.31:83/openUrl/efsdp4I/live.m3u8
     *            文件内容{
     *            #EXTM3U
     *            #EXT-X-VERSION:3
     *            #EXT-X-ALLOW-CACHE:NO
     *            #EXT-X-MEDIA-SEQUENCE:349
     *            #EXT-X-TARGETDURATION:2
     *            #EXTINF:2.000,
     *            seghik-349.ts
     *            #EXTINF:2.000,
     *            seghik-350.ts
     *            #EXTINF:2.000,
     *            seghik-351.ts
     *            }
     * @return ts文件路径集合
     */
    public List<String> parseStringFromUrl(String url) throws Exception {
        if (url == null) return null;
        LogUtils.d(TAG, "parseStringFromUrl:url=" + url);
        List<String> m3u8Result = new ArrayList<>();//m3u8文件路径集合
        List<String> tsResult = new ArrayList<>();//ts文件路径集合

        HttpURLConnection conn = null;
        BufferedReader reader = null;
        try {
            String host = url.substring(0, url.lastIndexOf("/") + 1);
            conn = (HttpURLConnection) new URL(url).openConnection();
            conn.setRequestMethod("GET");
            conn.setDoOutput(true);
            conn.setDoInput(false);
            conn.setConnectTimeout(1000 * 10);
            conn.connect();
            int responseCode = conn.getResponseCode();
            LogUtils.d(TAG, "parseStringFromUrl:responseCode=" + responseCode);
            if (responseCode == 200) {
                reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                String line;
                while ((line = reader.readLine()) != null) {
                    LogUtils.d(TAG, "parseStringFromUrl:" + line);
                    if (line.startsWith("#")) {
                    } else if (line.length() > 0 && line.contains(".m3u8")) {
                        //行业版
                        if (line.startsWith("http")) {
                            m3u8Result.add(line);
                        } else {
                            m3u8Result.add(host + line);
                        }
                    } else if (line.length() > 0 && line.contains(".ts")) {
                        //行业版
                        if (line.startsWith("http")) {
                            tsResult.add(line);
                        } else {
                            tsResult.add(host + line);
                        }
                    }
                }
            }else if(responseCode==302){
                //重定向
                String location = conn.getHeaderField("Location");
                conn.disconnect();
                LogUtils.d(TAG, "parseStringFromUrl:location="+location);
                if(location!=null){
                   return parseStringFromUrl(location);
                }
            }


        } finally {
            IOUtils.close(reader);
            if (conn != null) {
                conn.disconnect();
                conn = null;
            }
        }

        //使用递归方式解析m3u8
        if (m3u8Result.size() > 0) {
            for (String u : m3u8Result) {
                parseStringFromUrl(u);
            }
        }

        return tsResult;
    }

    /**
     * 下载ts文件,每个ts文件单独保存
     *
     * @param url
     * @throws Exception
     */
    private void downTs(Context context,String url) throws Exception {
        //http://112.45.116.31:83/openUrl/vFI97q0/seghik-1385.ts
//        LogUtils.d(TAG, "downTs:" + Thread.currentThread().getName());
        LogUtils.d(TAG, "downTs:url=" + url);
        String fileName = url.substring(url.lastIndexOf("/") + 1);
        File cacheFolder = FileUtils.getAppExternalCacheFolder(context,"ts");//保存ts文件的文件夹
        File tsFile = new File(cacheFolder, fileName);
        LogUtils.d(TAG, "downTs:tsFile=" + tsFile);
        if (tsFile.exists()) {
            LogUtils.d(TAG, "downTs:文件存在");
            return;
        }
        HttpURLConnection conn = null;
        InputStream ins = null;
        FileOutputStream fos = null;
        try {
            URL httpURL = new URL(url);
            conn = (HttpURLConnection) httpURL.openConnection();
            conn.setRequestMethod("GET");
            conn.setDoOutput(true);
            conn.setDoOutput(true);
            conn.setConnectTimeout(3 * 1000);
            conn.connect();
            if (200 == conn.getResponseCode()) {
                ins = conn.getInputStream();
                fos = new FileOutputStream(tsFile, true);
                byte[] buffer = new byte[4 * 1024];
                int readLen = 0;
                while ((readLen = ins.read(buffer)) != -1) {
                    fos.write(buffer, 0, readLen);
//                    LogUtils.d(TAG, "downTs:readLen=" + readLen);
                }
                fos.flush();
            }
        } finally {
            IOUtils.close(ins);
            IOUtils.close(fos);
            if (conn != null) {
                conn.disconnect();
                conn = null;
            }
        }

    }


    /**
     * 下载ts文件,保存到一个mp4文件中
     *
     * @param url
     * @throws Exception
     */
    private void downTsToMp4(String url) throws Exception {
        //http://112.45.116.31:83/openUrl/vFI97q0/seghik-1385.ts
//        LogUtils.d(TAG, "downTs:" + Thread.currentThread().getName());
        LogUtils.d(TAG, "downTsToMp4:url=" + url);
        //直接把ts片段文件保存为mp4文件
        if (finishUrl.contains(url)) {
//            LogUtils.d(TAG, "downTsToMp4:此url已经下载过了");
            return;
        }
        HttpURLConnection conn = null;
        InputStream ins = null;
        FileOutputStream fos = null;
        try {
            URL httpURL = new URL(url);
            conn = (HttpURLConnection) httpURL.openConnection();
            conn.setRequestMethod("GET");
            conn.setDoOutput(true);
            conn.setDoOutput(true);
            conn.setConnectTimeout(3 * 1000);
            conn.connect();
            if (200 == conn.getResponseCode()) {
                ins = conn.getInputStream();
                fos = new FileOutputStream(path, true);
                byte[] buffer = new byte[4 * 1024];
                int readLen = 0;
                int totalLen=0;
                while ((readLen = ins.read(buffer)) != -1) {
                    fos.write(buffer, 0, readLen);
                    finishSize += readLen;
                    totalLen+=readLen;
                    LogUtils.d(TAG, "downTs:totalLen=" + totalLen);
                    mainHandler.sendMessage(mainHandler.obtainMessage(CALLBACK, CALLBACK_ARGS1_PROGRESS, 0));
                }
                LogUtils.d(TAG, "downTsToMp4:下完一个ts " + finishSize);
                fos.flush();
                finishUrl.add(url);

            }
        } finally {
            IOUtils.close(ins);
            IOUtils.close(fos);
            if (conn != null) {
                conn.disconnect();
                conn = null;
            }
        }

    }

    /**
     * 下载m3u8连接中的所有ts片段合并成mp4
     */
    private void downAllTsInM3u8() {
        try {
            LogUtils.d(TAG, "downAllTsInM3u8:>>>>>>>>>>>>>downing=" + downSwitch);
            if (downing) {
                List<String> strings = parseStringFromUrl(url);
                if (downing && strings != null && strings.size() > 0) {
                    for (int i = 0; i < strings.size() && downing; i++) {
                        String s = strings.get(i);
                        downTsToMp4(s);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.e(TAG, "start:" + e.getMessage());
            if (e instanceof ConnectException) {
                //网络异常,发生错误且结束下载
                downing = false;
                downSwitch = false;
                mainHandler.sendMessage(mainHandler.obtainMessage(CALLBACK, CALLBACK_ARGS1_ERROR, 0, e.getMessage()));
            }
        }
    }

    private void reset() {
        finishUrl.clear();
        finishSize = 0;
    }

    public synchronized void start() {
        if (!downSwitch && !downing) {
            reset();
            downSwitch = true;
            downing = true;
            mainHandler.sendMessage(mainHandler.obtainMessage(CALLBACK, CALLBACK_ARGS1_START, 0));
            ThreadUtils.getExecutorService().execute(() -> {
                while (downSwitch) {
                    downAllTsInM3u8();
                }
                //停止下载后还需要下载最后一个片段,防止片段丢失
                if (downing) {
                    downAllTsInM3u8();
                }
                downing = false;
                downSwitch = false;
                mainHandler.sendMessage(mainHandler.obtainMessage(CALLBACK, CALLBACK_ARGS1_STOP, 0));

            });
        }
    }

    public synchronized void stop() {
        downSwitch = false;
    }

    public boolean isDowning() {
        return downing;
    }


    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public void setCallback(Callback callback) {
        this.callback = callback;
    }

    public interface Callback {
        /**
         * 开始下载
         **/
        void onStart();

        /***下载进度*/
        void onProgress(long finishSize);

        /***完成下载*/
        void onStop(String path);


        /***发生异常
         * @param error*/
        void onError(String error);
    }

}
